﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

namespace AzureStorageMapper.Criterias.TableStorage
{
    public class TableQueryUtils
    {
        public static string GetStringQuery(AbstractQuery query)
        {
            if (query is Criteria)
            {
                return FromCriteria(query as Criteria);
            }
            else if (query is Restrietion)
            {
                return FromRestrietion(query as Restrietion);
            }
            else if (query is ContinuationQuery)
            {
                return FromContinuation(query as ContinuationQuery);
            }
            else
                throw new NotSupportedException();
        }

        private static string FromRestrietion(Restrietion restrietion)
        {
            if (restrietion.Relation.Equals("and", StringComparison.InvariantCultureIgnoreCase)
                || restrietion.Relation.Equals("or", StringComparison.InvariantCultureIgnoreCase))
            {
                return string.Format("({0}) {1} ({2})", FromCriteria(restrietion.CriteriaOne),
                                                        restrietion.Relation,
                                                        FromCriteria(restrietion.CriteriaTwo));
            }
            else if (restrietion.Relation.Equals("not", StringComparison.InvariantCultureIgnoreCase))
            {
                return string.Format("not({0})", FromCriteria(restrietion.CriteriaOne));
            }
            else
                throw new NotSupportedException(string.Format("Invalid restrietion '{0}'", restrietion.Relation));
        }

        private static string FromCriteria(Criteria criteria)
        {
            StringBuilder sb = new StringBuilder();

            sb.Append(criteria.FieldColumn.Column);
            switch (criteria.FieldColumn.CriteriaOperation.Opt)
            {
                case CriteriaOperator.Equal:
                    sb.Append(" eq ");
                    break;
                case CriteriaOperator.GreaterThan:
                    sb.Append(" gt ");
                    break;
                case CriteriaOperator.GreaterThanOrEqual:
                    sb.Append(" ge ");
                    break;
                case CriteriaOperator.LessThan:
                    sb.Append(" lt ");
                    break;
                case CriteriaOperator.LessThanOrEqueal:
                    sb.Append(" le ");
                    break;
                case CriteriaOperator.NotEqual:
                    sb.Append(" ne ");
                    break;
                default:
                    throw new NotSupportedException(string.Format("Invalid operation {0}", criteria.FieldColumn.CriteriaOperation.Opt));
            }

            if (criteria.FieldColumn.CriteriaOperation.IsValueOperation)
                sb.Append(GetStringFromFieldValue(criteria.FieldColumn.CriteriaOperation.FieldValue));
            else
                sb.AppendFormat("({0})", FromCriteria(criteria.FieldColumn.CriteriaOperation.QuerySubCriteria));

            return sb.ToString();
        }

        private static string GetStringFromFieldValue(object p)
        {
            string val = null;

            if (p is DateTime)
            {
                val = string.Format("DateTime'{0}'", XmlConvert.ToString((DateTime)p, XmlDateTimeSerializationMode.RoundtripKind));
            }
            else if (p is Guid)
            {
                val = string.Format("Guid'{0}'", p.ToString());
            }
            else if (p is byte[])
            {
                val = UTF8Encoding.UTF8.GetString((byte[])p);
            }
            else
            {
                val = string.Format("'{0}'", p.ToString());
            }

            return val;
        }

        private static string FromContinuation(ContinuationQuery cont)
        {
            List<string> tokens = new List<string>();
            if (!string.IsNullOrWhiteSpace(cont.NextTableName))
            {
                tokens.Add(string.Format("NextTableName={0}", cont.NextTableName));
            }
            if (!string.IsNullOrWhiteSpace(cont.NextPartitionKey))
            {
                tokens.Add(string.Format("NextPartitionKey={0}", cont.NextPartitionKey));
            }
            if (!string.IsNullOrWhiteSpace(cont.NextRowKey))
            {
                tokens.Add(string.Format("NextRowKey={0}", cont.NextRowKey));
            }
            return string.Join("&", tokens);
        }
    }
}
